package com.common.utils;

import com.common.base.BasePO;
import com.common.base.CollectionUtils;
import com.common.utils.tree.FullPathFeature;
import com.common.utils.tree.SortDirection;
import com.common.utils.tree.SortFeature;
import com.common.utils.tree.TreeConfigure;
import net.sf.cglib.beans.BeanGenerator;
import net.sf.cglib.beans.BeanMap;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class TreeUtils {
    private static final String ID = "id";
    private static final String PID = "pid";
    private static final String CHILDREN = "children";

    private TreeUtils() {
        throw new UnsupportedOperationException("operation not support");
    }

    public static <T> List<T> getObject(List<T> list) {
        return CollectionUtils.isEmpty(list) ? Collections.emptyList() : convert2Tree(list, (Object)null, "id", "pid", "children", (String)null, (SortDirection)null, (String)null);
    }

    public static <T> List<T> convert2Tree(List<T> list, TreeConfigure configure) {
        SortDirection sortDirection = null;
        String sortProperty = null;
        String fullPathName = null;
        String idProperty = "id";
        String pidProperty = "pid";
        String childrenProperty = "children";
        Object topId = null;
        if (configure != null) {
            topId = configure.getTopId();
            if (StringUtils.isNotEmpty(configure.getIdProperty())) {
                idProperty = configure.getIdProperty();
            }

            if (StringUtils.isNotEmpty(configure.getPidProperty())) {
                pidProperty = configure.getPidProperty();
            }

            if (StringUtils.isNotEmpty(configure.getChildrenProperty())) {
                childrenProperty = configure.getChildrenProperty();
            }

            SortFeature sortFeature = configure.getSortFeature();
            if (sortFeature != null) {
                sortDirection = sortFeature.getSortDirection();
                if (StringUtils.isNotEmpty(sortFeature.getSortProperty())) {
                    sortProperty = sortFeature.getSortProperty();
                }
            }

            FullPathFeature fullPathFeature = configure.getFullPathFeature();
            if (fullPathFeature != null && StringUtils.isNotEmpty(fullPathFeature.getName())) {
                fullPathName = fullPathFeature.getName();
            }
        }

        return convert2Tree(list, topId, idProperty, pidProperty, childrenProperty, sortProperty, sortDirection, fullPathName);
    }

    public static <T> List<T> convert2Tree(List<T> list, Object topId, String idProperty, String pidProperty, String childrenProperty, String sortProperty, SortDirection sortDirection, String fullPathName) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        } else {
            Map<Object, T> allNodeMap = new HashMap(list.size() * 2);
            Class<?> clazz = list.get(0).getClass();
            List<Field> fieldList = new ArrayList();
            fieldList.addAll(Arrays.asList(clazz.getDeclaredFields()));
            fieldList.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
            HashMap properties = new HashMap();
            properties.put(childrenProperty, List.class);
            if (StringUtils.isNotEmpty(fullPathName)) {
                properties.put(fullPathName, String.class);
            }

            Iterator var12 = fieldList.iterator();

            while(var12.hasNext()) {
                Field field = (Field)var12.next();
                if (!"serialVersionUID".equals(field.getName())) {
                    properties.put(field.getName(), field.getType());
                }
            }

            TreeBeanHelper bean = new TreeBeanHelper(properties, clazz);
            clazz = bean.getObject().getClass();

            try {
                String idSuffix = idProperty.substring(0, 1).toUpperCase() + idProperty.substring(1);
                String pidSuffix = pidProperty.substring(0, 1).toUpperCase() + pidProperty.substring(1);
                String childrenSuffix = childrenProperty.substring(0, 1).toUpperCase() + childrenProperty.substring(1);
                Method getId = clazz.getMethod("get" + idSuffix);
                Method getPid = clazz.getMethod("get" + pidSuffix);
                Method getChildren = clazz.getMethod("get" + childrenSuffix);
                Method setChildren = clazz.getMethod("set" + childrenSuffix, List.class);
                Method getSort = null;
                Method getPath = null;
                Method setPath = null;
                String sortPropertySuffix;
                if (StringUtils.isNotEmpty(fullPathName)) {
                    sortPropertySuffix = fullPathName.substring(0, 1).toUpperCase() + fullPathName.substring(1);
                    getPath = clazz.getMethod("get" + sortPropertySuffix);
                    setPath = clazz.getMethod("set" + sortPropertySuffix, String.class);
                }

                if (StringUtils.isNotEmpty(sortProperty)) {
                    sortPropertySuffix = sortProperty.substring(0, 1).toUpperCase() + sortProperty.substring(1);
                    getSort = clazz.getMethod("get" + sortPropertySuffix);
                }

                Iterator var32 = list.iterator();

                while(var32.hasNext()) {
                    T node = (T)var32.next();
                    Object value = null;
                    TreeBeanHelper beanHelper = new TreeBeanHelper(properties, clazz);

                    Field field;
                    for(Iterator var27 = fieldList.iterator(); var27.hasNext(); beanHelper.setValue(field.getName(), field.get(node))) {
                        field = (Field)var27.next();
                        field.setAccessible(true);
                        if (idProperty.equals(field.getName())) {
                            value = field.get(node);
                        }
                    }

                    allNodeMap.put(value, (T)beanHelper.getObject());
                }

                return doConvert(allNodeMap, topId, getId, getPid, getChildren, setChildren, getSort, getPath, setPath, sortDirection);
            } catch (NoSuchMethodException | IllegalAccessException var29) {
                var29.printStackTrace();
                return Collections.emptyList();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    private static <T> List<T> doConvert(Map<Object, T> allNodeMap, Object topId, Method getId, Method getPid, Method getChildren, Method setChildren, Method getSort, Method getPath, Method setPath, SortDirection sortDirection) throws Exception {
        List<T>  topNodes = new ArrayList(allNodeMap.size());

        try {
            Iterator var11 = allNodeMap.keySet().iterator();

            Object node;
            while(var11.hasNext()) {
                node = var11.next();
                T node1 = allNodeMap.get(node);
                Object pid = getPid.invoke(node1, (Object[])null);
                Object parent = allNodeMap.get(pid);
                if (parent == null) {
                    topNodes.add(node1);
                } else {
                    List children = (List)getChildren.invoke(parent, (Object[])null);
                    if (children == null) {
                        children = new ArrayList();
                    }

                    ((List)children).add(node1);
                    if (getSort != null) {
                        sort((List)children, getSort, sortDirection);
                    }

                    setChildren.invoke(parent, children);
                }
            }
            topNodes = topId != null && allNodeMap.get(topId) != null ? (List)getChildren.invoke(allNodeMap.get(topId), (Object[])null) : topNodes;
            if (getSort != null) {
                sort((List)topNodes, getSort, sortDirection);
            }

            if (getPath != null && setPath != null) {
                var11 = ((List)topNodes).iterator();

                while(var11.hasNext()) {
                    node = var11.next();
                    setPath.invoke(node, getId.invoke(node, (Object[])null) + "");
                    fillPath(node, getChildren, getPath, setPath, getId);
                }
            }

            return (List)topNodes;
        } catch (InvocationTargetException | IllegalAccessException var17) {
            var17.printStackTrace();
            throw new Exception("生成树结构失败！");
        }
    }

    private static <T> void sort(List<T> list, Method getSort, SortDirection sortDirection) {
        Collections.sort(list, (currentNode, nextNode) -> {
            try {
                Object currentNodeOrder = getSort.invoke(currentNode, (Object[])null);
                if (currentNodeOrder instanceof Integer) {
                    Integer sorting1 = (Integer)getSort.invoke(currentNode, (Object[])null);
                    Integer sorting2 = (Integer)getSort.invoke(nextNode, (Object[])null);
                    return sortDirection != null && sortDirection == SortDirection.DESC ? (sorting2 == null ? 0 : sorting2) - (sorting1 == null ? 0 : sorting1) : (sorting1 == null ? 0 : sorting1) - (sorting2 == null ? 0 : sorting2);
                } else {
                    String sort = getSort.invoke(currentNode, (Object[])null) + "";
                    return sortDirection != null && sortDirection == SortDirection.DESC ? (getSort.invoke(nextNode, (Object[])null) + "").compareToIgnoreCase(sort) : sort.compareToIgnoreCase("" + getSort.invoke(nextNode, (Object[])null));
                }
            } catch (InvocationTargetException | IllegalAccessException var7) {
                var7.printStackTrace();
                throw new RuntimeException(var7);

            }
        });
    }

    private static void fillPath(Object parentNode, Method getChildren, Method getPath, Method setPath, Method getId) throws InvocationTargetException, IllegalAccessException {
        if (parentNode != null) {
            List children = (List)getChildren.invoke(parentNode, (Object[])null);
            if (CollectionUtils.isNotEmpty(children)) {
                Iterator var6 = children.iterator();

                while(var6.hasNext()) {
                    Object node = var6.next();
                    setPath.invoke(node, getPath.invoke(parentNode, (Object[])null) + "," + getId.invoke(node, (Object[])null));
                    fillPath(node, getChildren, getPath, setPath, getId);
                }
            }
        }

    }

    public static void main(String[] args) {
        List<User> list = new ArrayList();
        list.add(new User(1, 0, 9, "name"));
        list.add(new User(2, 0, 8, "name"));
        list.add(new User(3, 2, 7, "name"));
        list.add(new User(4, 0, 6, "name"));
        list.add(new User(5, 1, 5, "name"));
        list.add(new User(6, 0, 4, "name"));
        list.add(new User(7, 3, 3, "name"));
        list.add(new User(8, 0, 2, "name"));
        list.add(new User(9, 7, 1, "name"));
        list.add(new User(10, 0, 0, "name"));
        list.add(new User(11, 1, 11, "name"));
        Long start = System.currentTimeMillis();
        convert2Tree(list, TreeConfigure.builder().topId(0).build());
        System.out.println(System.currentTimeMillis() - start);
    }

    static class User extends BasePO {
        private int pid;
        private int sort;
        private String name;

        public User() {
        }

        public User(Integer id, int pid, int sort, String name) {
            super(id, new Date(), new Date(), "", id, "", false, 1);
            this.pid = pid;
            this.sort = sort;
            this.name = name;
        }

        public int getPid() {
            return this.pid;
        }

        public void setPid(int pid) {
            this.pid = pid;
        }

        public int getSort() {
            return this.sort;
        }

        public void setSort(int sort) {
            this.sort = sort;
        }

        public String getName() {
            return this.name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    static class Person<T> extends BasePO {
        private int user;
        private int account;

        public Person() {
        }

        public Person(Integer id, Date createTime, Date updateTime, String creator, Integer creatorId, String updator, Boolean deleted, Integer version, int user, int account) {
            super(id, createTime, updateTime, creator, creatorId, updator, deleted, version);
            this.user = user;
            this.account = account;
        }
    }

    private static class TreeBeanHelper {
        private Object object = null;
        private BeanMap beanMap = null;

        TreeBeanHelper() {
        }

        TreeBeanHelper(Map<String, Class> properties, Class<?> sourceType) {
            this.object = this.generateBean(properties, sourceType);
            this.beanMap = BeanMap.create(this.object);
        }

        void setValue(String property, Object value) {
            this.beanMap.put(property, value);
        }

        Object getValue(String property) {
            return this.beanMap.get(property);
        }

        Object getObject() {
            return this.object;
        }

        Object generateBean(Map<String, Class> properties, Class<?> sourceType) {
            BeanGenerator generator = new BeanGenerator();
            generator.setSuperclass(sourceType);
            Set keySet = properties.keySet();
            Iterator i = keySet.iterator();

            while(i.hasNext()) {
                String key = (String)i.next();
                generator.addProperty(key, (Class)properties.get(key));
            }

            return generator.create();
        }
    }
}
